#include "../include/defs.h"
#include "../include/kc.h"
#include "../include/sleep.h"

#define STACK_USUARIOS  0x1000
#define DEF_PRIORITY    3

extern PROCESO procesos[];
extern peticion peticionesDormir[];
extern TTY terminales[];

extern int terminalActual;
extern int pidActual;
extern int proxPid;

extern unsigned long *page_directory;
extern unsigned long *page_table;






int test(int argc, char **argv)
{ 
   char * a = (char *) 0xF00000;
   int i = 0;
   while( i < 99999)
   a[i++] = 'a';

}




void
sleep (int time) {
    int i, j;

    if (time <= 0)
        return;

    for (i = 0; i < MAXPROCESOS; i++) {
        if (peticionesDormir[i].time == 0) {
            /*Agrego una entrada al array de procesos
               que quieren dormir */
            peticionesDormir[i].pid = pidActual;

            peticionesDormir[i].time = time * TIMECONSTANT;

            /*Tengo que buscar proceso para luego bloquearlo */
            bloqueaProceso (pidActual, ESPERANDO_SLEEP);
            switch_manual ();

            return;
        }
    }
}
int
ProcesoExiste(int pid)
{
    int i;

    if (pid == 0)
        return 1;

    for (i = 0; i < MAXPROCESOS; i++) {
        if (procesos[i].pid == pid && !procesos[i].free_slot)
            return 1;
    }

    return 0;

}

PROCESO *
GetProcessByPid (int pid) {
    int i;

    for (i = 0; i < MAXPROCESOS; i++) {
        if (procesos[i].pid == pid && !procesos[i].free_slot)
            return &procesos[i];
    }

    return 0;
}

PROCESO*
GetProcessByPosition(int i)
{
    return &procesos[i];
}

PROCESO *
GetProcessByName (char *proceso) {
    int i;
    int len1, len2;

    len1 = strlen (proceso);

    for (i = 0; i < MAXPROCESOS; i++) {
        len2 = strlen (procesos[i].nombre);

        if (strncmp (proceso, procesos[i].nombre, len1) == 0 && len1 == len2
            && !procesos[i].free_slot) {
            return &procesos[i];
        }
    }

    return 0;
}

int
NoHayProcesos (void) {
    int i;

    for (i = 0; i < MAXPROCESOS; i++) {
        if (i == GOD)
            continue;
        if (procesos[i].free_slot == 0 && !procesos[i].bloqueado)
            return 0;
    }
    return 1;
}

int
GetPid (void) {
    return proxPid++;
}

void
Limpia (void) {
    /* deberia matar al proceso y toda la bola */
    PROCESO *temporal;
    PROCESO *padre;

    temporal = GetProcessByPid (pidActual);

    if (temporal->padre != GOD) {
        padre = GetProcessByPid (temporal->padre);
        padre->bloqueado = 0;
    }
    temporal->free_slot = 1;
    while (1) {
        asm volatile ("hlt");
    }
}

void
exec (char *nombre, int (*proceso) (int argc, char **argv), int enBackground) {
    CrearProceso (nombre, proceso, terminalActual, 0, (char **) 0,
                  DEF_PRIORITY, enBackground, STACK_USUARIOS);
}

void
CrearProceso (char *nombre, int (*proceso) (int argc, char **argv), int tty,
              int argc, char **argv, int prioridad, int enBackground,
              int stacksize) {
    int i;

    void *stack;
    void *heap;
    
 
    stack = Malloc (stacksize);
    heap =  Malloc (HEAP_SIZE);

    for (i = 1; i < MAXPROCESOS; i++) {


        if (procesos[i].free_slot)
            break;
    }
    


    procesos[i].pid = GetPid ();
    procesos[i].background = enBackground;
    procesos[i].prioridad = prioridad;
    strncpy (procesos[i].nombre, nombre, strlen (nombre)+1);
    procesos[i].bloqueado = 0;
    procesos[i].terminal = tty;
    procesos[i].stacksize = stacksize;
    procesos[i].stackstart = (int) stack + stacksize - 1;
    procesos[i].heapsize = HEAP_SIZE;
    procesos[i].heapstart = (int) heap;
    procesos[i].nextfree = (int) heap;
    
    /*Levanto las paginas de este proceso para poder armar stack*/         
    levantaPaginas((PROCESO *)&procesos[i]);
    
    procesos[i].ESP =
        ArmaStackFrame (proceso, procesos[i].stackstart, Limpia);

  
     /*Bajo las paginas para evitar que cualquier otro proceso escriba 
    en la zona de este*/
    bajarPaginas((PROCESO*)&procesos[i]);
    
    procesos[i].padre = pidActual;

    if (!enBackground && pidActual > 0) {
        PROCESO *proc = GetProcessByPid (pidActual);
        /* bloqueado esperando que termine su hijo */
        proc->bloqueado = ESPERANDO_HIJO;
        procesos[i].padre = pidActual;
    }
    procesos[i].free_slot = 0;
    
   
  
}

void
Kill (int pid) {
    PROCESO * proc;
    PROCESO * padre;
    if (pid != GOD) {
        proc = GetProcessByPid (pid);
        if (proc != 0) {
            printf ("El proceso ", 11);
            printf (proc->nombre, strlen (proc->nombre));
            printf (" fue eliminado.\n", 16);
            proc->free_slot = 1;
            proc->bloqueado = 0;
            proc->pid = -1;
            matarHijos (pid);
            if(proc->padre!=0)
            {
                padre = GetProcessByPid (proc->padre);
                if (proc->padre != GOD )
                {
                    if(padre->padre != 0 && !proc->background )
                        desbloqueaProceso(proc->padre);
                    else if(padre->terminal==terminalActual && !proc->background )
                        desbloqueaProceso(proc->padre);
                    else
                    { 
                           if( ! proc->background )
		           {
			    desbloqueaProceso(proc->padre);
                            bloqueaProceso (proc->padre, 1);
			   }
                    }
                }
            }
            else if (proc->padre != GOD)
                desbloqueaProceso(proc->padre);
        }
        else
            printf ("Proceso no encontrado.\n", 23);
    }
    else {
        printf ("Proceso protegido.\n", 19);
    }
    return;
}

int
Nada (int argc, char **argv) {
    while (1)
    {
        asm volatile ("hlt");
    }
}

int procesosex[MAXPROCESOS];

void
matarHijos (int pidPadre) {

    int i;
    for (i = 0; i < MAXPROCESOS; i++) {
        /*Solo mata los hijos que no estan en background */
        if (procesos[i].padre == pidPadre && !procesos[i].background) {
            procesos[i].free_slot = 0;
            Kill (procesos[i].pid);
        }
    }
    return;
}

void
Renice (int pid, int prioridad) {
    PROCESO *proc = GetProcessByPid (pid);
    if (proc != 0) {
        if (prioridad >= 0 && prioridad < CANT_PRIOR) {
            proc->prioridad = prioridad;
            printf ("Prioridad del proceso ", 22);
            printf (proc->nombre, strlen (proc->nombre));
            printf (" cambiada.\n", 11);
        }
        else
            printf ("Prioridad Invalida.\n", 20);

    }
    else {
        printf ("Proceso no encontrado.\n", 23);
    }
}
